Android 个性化 之 百变 Dialog

title: ### 前言
  关于Dialog的使用可谓是相当的广泛,可以用来做各种处理,其中也衍生了许多种的处理方式,有好有坏,自己有必要加以拿捏,熟练了以后,便可谓是招之即来,挥之即去。
  Dialog搭配一些个性化风格与简单动画会有一些神奇效果(Pics from Baidu_Pics):

dialog1.gif
  实现类似的展示效果并不难,接下来会在页面的基础上加入一些回调设置的说明,在开始之后还是先来看下其继承结构吧:

java.lang.Object    
  ↳ android.app.Dialog

-->implements DialogInterface, KeyEvent.Callback, View.OnCreateContextMenuListener, Window.Callback

  由此可知,Dialog是一个可以独立的控件,而DialogInterface和KeyEvent.Callback也说明其是一个回调性很强的Object,在诸多的Dialog实例中,其子类AlertDialog是一种很好的实现方式,下面会具体介绍。

Dialog的初始化

  Dialog和普通的View不同,它有自己的生命周期。

  • 通过onCreate( )创建

    @Override
    protected void onCreate(Bundle savedInstanceState) {    
          super.onCreate(savedInstanceState);    
          setContentView(R.layout.my_dialog);    
    }
    

      可以看出和Activity的onCreate( )类似,也可以在其中加入一些init( )方法对layout文件中的子控件进行监听等。下面看一下调用方式:

    MyDialog myDialog = new MyDialog(this);
    myDialog.setCanceledOnTouchOutside(false);
    myDialog.show();
    // 具体逻辑
    
  • 通过构造方法创建

    public MyDialog(Activity activity, int resLayout) {    
          super(activity, R.style.myDialog);    
          this.activity = activity;
          this.resLayout = resLayout;
    }
    

      构造方法中的参数可以根据需求来定义,一般不超过5个,不然会影响性能,下面看一下调用方式:

    MyDialog myDialog = new MyDialog(this, R.style.myDialog, R.layout.my_dialog);
    myDialog.show();
    
  • 创建与销毁时的细节
      而这两种创建方法却有一点不同:onCreate( )创建的Dialog的逻辑处理需在show( )之后,相当于调用了show( )之后才会真正的创建出来,而通过构造方法创建的Dialog的show( )可以放在最后的位置,相当于一个整体的展示。
      而一般自定义的Dialog由于个性化需求较大,所以其style一般需要简单定制一下:

    <!-- 自定义Dialog的Theme定义 -->
    <style name="myDialog" parent="android:Theme.Dialog">       
          <item name="android:windowFrame">@null</item>
          <item name="android:windowNoTitle">true</item>
          <item name="android:windowIsFloating">true</item>
          <item name="android:windowContentOverlay">@null</item>
          <item name="android:windowBackground">@android:color/transparent</item>
    </style>
    

      对于Dialog的销毁,有两种方法:dismiss( )和cancel( ),仔细的童鞋会发现cancel( )其实中调用了dismiss( )的,只是加了一个对mCancelMessage的判断,看下该源码:

    public void setOnCancelListener(final OnCancelListener listener) {  
          if (listener != null) {  
                mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);  
          } else {  
                mCancelMessage = null;  
          }  
    }  
    
    public void setCancelMessage(final Message msg) {  
          mCancelMessage = msg;  
    }  
    

      也就是说,如果调用了setOnCancelListener,这个mCancelMessage变量有作用,否则dismiss( )和cancel( )等同。
      此外,还可以对Dialog的透明,展出方向(上下左右)等进行设置,如下图便是一个底部弹出的Dialog:
    dialog_bottom.png

###Dialog的回调监听
  Keep your eyes on……重点来了,关于Dialog最重要的除了展示效果外无非就是其回调的设置了,而其回调方式有很多种,有把Dialog当作Activity的方式来处理的,有使用DialogInterface来处理的,有把Dialog当作View的方式来处理,还有使用loop/handler的方式来处理的……总之有很多种方式,具体的逻辑与效率总有优劣之处,请大家自己掌控~下面来一一介绍:

  • Dialog VS Handler
      使用Handler的方式,可以通过构造函数传递参数,然后在消息队列中捕获并处理,这种方式比较简洁,在调用的位置加上:

    myDialog.setHandler(mHandler);
    

      在Dialog中:

    private void setHandler(mHandler) {
          this.mHandler = mHandler;
    }
    

      然后便是在Activity中通过handleMessage(msg)方法进行处理,当然可以在此进行优化,使用静态内部类InnerHandler + 弱引用WeakReference的方式(附参考链接),将具体的回调设置在此处处理。

  • Dialog VS DialogFragment
      DialogFragment集Dialog与Fragment于一身,貌似很强大。DialogFragment配合DialogInterface使用比较切合,在调用的位置:

    MyDialog myDialog = new MyDialog(this, R.style.myDialog, R.layout.my_dialog);
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    myDialog.show(fragmentTransaction, null);
    

      而此时的MyDialog需要继承自DialogFragment,即按Fragment的方式来初始化Dialog,在我们需要回调处理的地方:

    MyActivity instance = (MyActivity) getActivity();
    instance.onDialogBack();
    

      即相当于回调了Activity中的onDialogBack( )方法:

    public void onDialogBack() {   
          // 具体逻辑
    }
    
  • Dialog VS setListener( )
      看过我以前自定义View的童鞋一定也了解其实现思路,即在Dialog中加入一些setListener( )方法,然后在实例化后直接该用内部的setListener( )方法即可,两种创建方式都可行,只需将myDialog对象调用MyDialog中的方法:

    Dialog中:
    public void setMyVisibility(boolean visibility) {    
          mView.setVisibility(visibility ? View.VISIBLE : View.GONE);
    }
    
    Activity中:
    myDialog.setMyVisibility(true);
    
  • Example
      结合了以上各个思路,下面给出两种可以拿来封装的样式,底部弹出的Dialog与居中弹出的Dialog:
    dialog_style1.png
    dialog_style2.png

###AlertDialog初探
  AlertDialog是一种极其个性化的Dialog,相当于一个样式封装好的Dialog,便于调用,默认的风格便是一种最简单的处理,当然也可以自定义,在其初始化时使用了强大且神奇的Build-建造者模式
  AlertDialog的构造方法全部是Protected的,所以不能直接通过new一个AlertDialog来创建出一个AlertDialog,需要用到AlertDialog.Builder中的create()方法:

Dialog alertDialog = new AlertDialog.Builder(this)   
      .setTitle("Title")                                    // 标题
      .setMessage("Content")                                // 内容
      .setPositiveButton("OK", this)                        // Positive Button
      .setNegativeButton("Cancel", this);                   // Negative Button
      .setNeutralButton("Neutral", this);                   // Neutral Button
      .setItems(new String[] {"A", "B", "C"}, this);        // 条目
      .setIcon(R.drawable.ic_launcher)                      // 图标
      .create();
alertDialog.show(); 

  因为是建造者模式,所以上面这些Build的内容都是Optional的,而其中若想添加监听,可以这样实现:

 .setPositiveButton("OK", new DialogInterface.OnClickListener() {                                                    
      @Override                     
      public void onClick(DialogInterface dialog, int which) {                         
            // TODO Auto-generated method stub                      
      }                 
})

  针对于其中的setItem( ),也可以有这样的扩展,效果如下:

.setSingleChoiceItems(new String[] {"A", "B", "C", "D"}, 0, this);       // 单选条目      

single_item.png

.setMultiChoiceItems(new String[] {"A", "B", "C", "D"}, 0, this);        // 多选条目

multi_item.png
  此外值得一说的是,若是继承了DialogFragment,则在使用了AlertDialog的setButton( )后,可以重写父类的onClick( )方法直接回调,感觉很强大的样子:

@Override
public void onClick(DialogInterface dialog, int which) {    
       switch(which) {
              case AlertDialog.BUTTON_NEGATIVE:
                     // TODO
                     break;
              case AlertDialog.BUTTON_NEGATIVE:
                     // TODO
                     break;
              // TODO
       }    
}

  当然AlertDialog还有很多方法,这里就不一一介绍了,其中pedant大神对其有仔细研究,有兴趣的可以去看看他的sweet-alert-dialog。我以前也参考过一些用例,简单写过一个AlertDialogDemo,大家也可以参考一下~

尾声

  关于Dialog这部分自己是一点一点踩过了许多坑,然后总结了许多种实现方式从而最终Get到其强大之处,由此写来给大家分享一下小小的心得~

  最后再来看一张很好看的效果~
dialog_sweet.gif